#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
#include <set>
#include <map>
#include <memory>
using namespace std;
// 详见书本《C++Primer第五版》p163
class QueryResult{
public:
    QueryResult(const string & word,
                shared_ptr<set<int>> line_no,
                shared_ptr<vector<string>> file)
        :_word(word),_line_no(line_no),_file(file)
    {}

    set<int>::iterator begin() {return _line_no->begin();}
    set<int>::iterator end() {return _line_no->end();}
    shared_ptr<set<int>> get_line_no() {return _line_no;}
    shared_ptr<vector<string>> get_file() {return _file;}

    void print(){
        cout << _word <<"出现了"<< _line_no->size()<<"次" <<endl;
        for (auto i : *_line_no){
     
            cout << i<<":" <<(*_file)[i-1] << endl;
        }
    }

private:
    string _word;
    shared_ptr<set<int>> _line_no;
    shared_ptr<vector<string>> _file;
};

class TextQuery{
public:
    bool is_word(string & word);
    TextQuery(const string & filename); //将文件按行保存，并记录单词行号
    QueryResult query(const string& word) const;//按单词查找

private:
    shared_ptr<vector<string>> _file;
    map<string,shared_ptr<set<int>>> line_no;
};

class Query;
class Query_base{
public:
    friend class Query; //Query需要调用Query_base的eval,rep
protected:
    virtual ~Query_base() = default;
private:
    virtual QueryResult eval(const TextQuery&) const = 0; //返回结果
    virtual string rep() const = 0;
};

//查找单个字符串,最简单的查找，也是其他查找的基础
class WordQuery:public Query_base{
    friend class Query; // 本类所有成员均为私有，访问需设为友元

    WordQuery(const string & s):_word(s){}
    QueryResult eval(const TextQuery& t) const{ return t.query(_word);}
    string rep() const {return _word;}
    string _word;
};
//保存Query_base的指针，对外的统一接口
class Query{
public:

    friend Query operator&(const Query& lhs, const Query& rhs);
    friend Query operator|(const Query& lhs,const Query& rhs);
    friend Query operator~(const Query& query);

    Query(const string & word): q(new WordQuery(word)){}

    QueryResult eval(const TextQuery & t) const{ return q->eval(t);}
    string rep() const {return q -> rep();}
private:
    Query(shared_ptr<Query_base> qu): q(qu) {} //and,or,not类中函数的返回值会构造Query
    shared_ptr<Query_base> q;
};

//andQuery、orQuery的父类
class BinaryQuery:public Query_base{
protected:
    //此处Query保存的是wordQuery类的指针
    BinaryQuery(const Query& left,const Query& right,string s)
        :lhs(left),rhs(right),opSym(s) {}

    string rep() const {return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";}

    Query lhs,rhs;
    string opSym;
};

class AndQuery : public BinaryQuery{

    friend Query operator&(const Query& lhs, const Query& rhs); //&会构造AndQuery

    AndQuery(const Query& left, const Query& right):
        BinaryQuery(left,right,"&") {}
    QueryResult eval(const TextQuery&) const;
};

Query operator&(const Query& lhs, const Query& rhs){
    return shared_ptr<Query_base>(new AndQuery(lhs,rhs));
}

QueryResult AndQuery::eval(const TextQuery & text) const{
    //此时的rhs，lhs实际为wordQuery，返回结果为QueryResult类型
    QueryResult right = rhs.eval(text), left = lhs.eval(text);
    auto ret_lines = make_shared<set<int>>();

    set_intersection(left.begin(),left.end(),right.begin(),right.end(),
                     inserter(*ret_lines,ret_lines->begin()));
    return QueryResult(rep(),ret_lines,left.get_file());
}

class OrQuery:public BinaryQuery{
    friend Query operator|(const Query& lhs,const Query& rhs);
    OrQuery(const Query& left,const Query& right):
        BinaryQuery(left,right,"|"){}
    QueryResult eval(const TextQuery&) const;
};

Query operator|(const Query& lhs,const Query& rhs){
    return shared_ptr<Query_base>(new OrQuery(lhs,rhs)); 
}

QueryResult OrQuery::eval(const TextQuery& text) const{
    QueryResult left = lhs.eval(text),right = rhs.eval(text);
    auto ret_lines = make_shared<set<int>>(left.begin(),left.end()); 
    ret_lines->insert(right.begin(),right.end());
    return QueryResult(rep(),ret_lines,left.get_file());
}

class NotQuery:public Query_base
{
    friend Query operator~(const Query& query);
    NotQuery(const Query& query):q(query){}
    QueryResult eval(const TextQuery&) const;
    string rep() const {return "~(" + q.rep()+")";}
    Query q;
};

Query operator~(const Query& query){
    return shared_ptr<Query_base>(new NotQuery(query));
}

QueryResult NotQuery::eval(const TextQuery& text) const{
    QueryResult ret = q.eval(text);
    auto ret_lines = make_shared<set<int>>();
    int line_size = ret.get_file()->size();
    auto _line_no = ret.get_line_no();
    for(int i = 1;i <= line_size;++i){
        if(_line_no->find(i) == _line_no->end()){ //在找到的行数中没找到
            ret_lines->insert(i);
        }
    }
    return QueryResult(rep(),ret_lines,ret.get_file());
}

void test(){
    TextQuery text("test.txt");
    Query q = (~Query("she")) & Query("hair") | Query("he") ;
    auto ret = q.eval(text);
    ret.print();
}

int main(int argv,char** args)
{
    if(argv == 1){cout << "no filename!";return -1;}
    TextQuery text(args[1]);
    while(true){
        cout << "please input your search word or input q to exit:" << endl;
        string word;
        cin >> word; 
        if (word == "q"){
            break;
        }
        Query q = Query(word);
        auto ret = q.eval(text); 
        ret.print();
    }
    return 0;
}

//----------------------------------
bool TextQuery::is_word(string & word){
    for(auto &c : word){
        if(c == ',' || c == '.'){
            word.pop_back();
            return true;
        }
        if(!isalpha(c)){
            return false;
        }
    }
    return true;
}
TextQuery::TextQuery(const string & filename)
{
    //cout << "TextQuery(filename)" << endl;
    _file = make_shared<vector<string>>(); //初始化_file
                                           //
    ifstream ifs(filename);
    string line;
    int line_count = 1;
    while(getline(ifs,line)){
        //cout << line << endl;
        _file->push_back(line);
        istringstream iss(line);
        string word;
        while(iss >> word){
            if(is_word(word)){
                auto& lines = line_no[word];
                if(!lines){ //此时关键词未初始化
                    //lines.reset(new set<int>);
                    lines = make_shared<set<int>>();
                }
                lines->insert(line_count);
            } 
        }
        ++line_count;
    }
}
QueryResult TextQuery::query(const string& word) const{
    //cout << "query:" << word <<endl; 
    auto ret = line_no.find(word);

    if(ret != line_no.end()){
        return QueryResult(word,ret->second,_file);
    }
    static shared_ptr<set<int>> nodata(new set<int>);
    return QueryResult(word,nodata,_file);
}
